package org.baicaixiaozhan.learn.guava.collect;

import com.google.common.collect.*;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.primitives.Ints;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * DESC: Guava 对 {@link Collections} 的扩展工具类不完全示例
 *
 * @author baicaixiaozhan
 * @since 2020/11/13
 */
public class CollectionsUtilitiesDemo {

    public static void main(String[] args) {
        maps();
    }

    public static void staticConstructors() {
        // JDK List
        List<String> list = Collections.emptyList();
        list = new ArrayList<>();
        list = new ArrayList<>(100);
        // Guava Lists
        list = Lists.newArrayList();
        list = Lists.newArrayListWithCapacity(100);

        // JDK Set
        Set<String> set = Collections.emptySet();
        set = new HashSet<>();
        // Guava Sets
        set = Sets.newHashSet();
    }

    public static void lists() {
        List<Integer> countUp = Ints.asList(1, 2, 3, 4, 5);

        List<Integer> countDown = Lists.reverse(countUp); // {5, 4, 3, 2, 1}
        countDown.forEach(System.out::println);

        // 分区重组
        List<List<Integer>> parts = Lists.partition(countUp, 2); // {{1, 2}, {3, 4}, {5}}
        parts.forEach(System.out::println);
    }

    public static void sets() {
        Set<String> wordsWithPrimeLength = ImmutableSet.of("one", "two", "three", "six", "seven", "eight");
        Set<String> primes = ImmutableSet.of("two", "three", "five", "seven");

        Sets.SetView<String> intersection = Sets.intersection(primes, wordsWithPrimeLength); // contains "two", "three", "seven"
        print.accept(intersection);

        // 许多次复用可考虑使用 immutableCopy()
        Set<String> copy = intersection.immutableCopy();
        print.accept(copy);

        Set<String> animals = ImmutableSet.of("gerbil", "hamster");
        Set<String> fruits = ImmutableSet.of("apple", "orange", "banana");

        // 笛卡尔积
        Set<List<String>> product = Sets.cartesianProduct(animals, fruits);
        print.accept(product);
        // {{"gerbil", "apple"}, {"gerbil", "orange"}, {"gerbil", "banana"},
        //  {"hamster", "apple"}, {"hamster", "orange"}, {"hamster", "banana"}}

        // 获取该 set 下所有的子集
        Set<Set<String>> animalSets = Sets.powerSet(animals);
        animalSets.forEach(System.out::print);
        // {{}, {" gerbil"}, {"hamster"}, {"gerbil", "hamster"}}
    }

    public static void maps() {
        // Maps.uniqueIndex 对标唯一 index
        ImmutableMap<Integer, String> stringsByIndex = Maps.uniqueIndex(Lists.newArrayList("apple", "orange"), getStrLength::apply);

        Multimap<Integer, String> stringsByIndexes = Multimaps.index(Lists.newArrayList("apple", "orange", "banana"), getStrLength::apply);

        System.out.println(stringsByIndex.toString()); // {5=apple, 6=orange}

        System.out.println(stringsByIndexes.toString()); // {5=[apple], 6=[orange, banana]}
        stringsByIndexes.get(6).forEach(System.out::println); // stringsByIndexes.get() => Collection


        // Maps.difference(Map, Map) 对比两个 map 的不同
        Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
        Map<String, Integer> right = ImmutableMap.of("b", 2, "c", 4, "d", 5);
        MapDifference<String, Integer> diff = Maps.difference(left, right);

        // entriesInCommon : 两个 Map 中均具有匹配的键和值
        Map<String, Integer> entriesInCommon = diff.entriesInCommon();// {"b" => 2}
        print.accept(entriesInCommon.toString());

        // entriesDiffering : 两个 Map 具有相同的键，但值不同
        Map<String, ValueDifference<Integer>> entriesDiffering = diff.entriesDiffering(); // {"c" => (3, 4)}
        print.accept(entriesDiffering.toString());

        // entriesOnlyOnLeft : 返回其键在左侧但不在右侧 Map 映射中的属性
        Map<String, Integer> entriesOnlyOnLeft = diff.entriesOnlyOnLeft(); // {"a" => 1}
        print.accept(entriesOnlyOnLeft.toString());

        // entriesOnlyOnRight : 返回其键在右侧但不在左侧 Map 映射中的属性
        Map<String, Integer> entriesOnlyOnRight = diff.entriesOnlyOnRight(); // {"d" => 5}
        print.accept(entriesOnlyOnRight.toString());
    }

    static Function<String, Integer> getStrLength = s -> s != null ? s.length() : 0;

    static Consumer<Object> print = System.out::println;
}
